MD5(str, cases := False)
{
    #DllLoad 'advapi32.dll'

    MD5_CTX := Buffer(104,0)
    DllCall("advapi32\MD5Init", "Ptr", MD5_CTX)
    DllCall("advapi32\MD5Update", "Ptr", MD5_CTX, "AStr", str, "UInt", StrLen(str))
    DllCall("advapi32\MD5Final", "Ptr", MD5_CTX)

    static MD5_DIGEST_LENGTH := 16
    o := ''
    loop MD5_DIGEST_LENGTH
        o .= Format("{:02" (cases ? "X" : "x") "}", NumGet(MD5_CTX, 87 + A_Index, "UChar"))

    return o
}


; MD5(string, encoding := "UTF-8")
; {
;     return CalcStringHash(string, 0x8003, encoding)
; }

; ; CalcAddrHash ======================================================================
; CalcAddrHash(addr, length, algid, &hash := 0, &hashlength := 0)
; {
;     static h := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f"]
;     static b := h.minIndex()
;     hProv := hHash := o := ""
;     if (DllCall("advapi32\CryptAcquireContext", "Ptr*", hProv, "Ptr", 0, "Ptr", 0, "UInt", 24, "UInt", 0xf0000000))
;     {
;         if (DllCall("advapi32\CryptCreateHash", "Ptr", hProv, "UInt", algid, "UInt", 0, "UInt", 0, "Ptr*", hHash))
;         {
;             if (DllCall("advapi32\CryptHashData", "Ptr", hHash, "Ptr", addr, "UInt", length, "UInt", 0))
;             {
;                 if (DllCall("advapi32\CryptGetHashParam", "Ptr", hHash, "UInt", 2, "Ptr", 0, "UInt*", hashlength, "UInt", 0))
;                 {
;                     VarSetStrCapacity(&hash, hashlength)
;                     if (DllCall("advapi32\CryptGetHashParam", "Ptr", hHash, "UInt", 2, "Ptr", &hash, "UInt*", hashlength, "UInt", 0))
;                     {
;                         loop hashlength
;                         {
;                             v := NumGet(hash, A_Index - 1, "UChar")
;                             o .= h[(v >> 4) + b] h[(v & 0xf) + b]
;                         }
;                     }
;                 }
;             }
;             DllCall("advapi32\CryptDestroyHash", "Ptr", hHash)
;         }
;         DllCall("advapi32\CryptReleaseContext", "Ptr", hProv, "UInt", 0)
;     }
;     return o
; }

; ; CalcStringHash ====================================================================
; CalcStringHash(string, algid, encoding := "UTF-8", &hash := 0, &hashlength := 0)
; {
;     chrlength := (encoding = "CP1200" || encoding = "UTF-16") ? 2 : 1
;     length := (StrPut(string, encoding) - 1) * chrlength
;     VarSetStrCapacity(&data, length)
;     StrPut(string, &data, floor(length / chrlength), encoding)
;     return CalcAddrHash(&data, length, algid, &hash, &hashlength)
; }